#import "MExpr.h"
#import "MMathLink.h"

@interface MStringExpr : MExpr
{
	NSString* _string;
}
@end

@implementation MStringExpr

- (id) initWithString: (NSString*) string
{
	NSParameterAssert(string);
	
	self = [super init];
	_string = [string copy];
	return self;
}

- (void) dealloc
{
	[_string release];
	[super dealloc];
}

- (NSString*) string
{
	return _string;
}

- (MExpr*) functionPart: (int) part
{
	return (part == 0 ? MSymbol(@"String") : nil);
}

- (BOOL) put: (MMathLink*) link
{
	return [link putString: _string];
}

- (BOOL) isEqual: (id) object
{
	return [object isKindOfClass: [MStringExpr class]]
		&& [_string isEqual: ((MStringExpr*) object)->_string];
}

- (unsigned) hash
{
	return ~[_string hash];
}

- (NSString*) description
{
	return [NSString stringWithFormat: @"\"%@\"", _string];
}

@end


@interface MSymbolExpr : MExpr
{
	NSString* _symbol;
}
@end

@implementation MSymbolExpr

- (id) initWithSymbol: (NSString*) symbol
{
	NSParameterAssert(symbol);
	
	self = [super init];
	_symbol = [symbol copy];
	return self;
}

- (void) dealloc
{
	[_symbol release];
	[super dealloc];
}

- (NSString*) symbol
{
	return _symbol;
}

- (MExpr*) functionPart: (int) part
{
	return (part == 0 ? MSymbol(@"Symbol") : nil);
}

- (BOOL) put: (MMathLink*) link
{
	return [link putSymbol: _symbol];
}

- (BOOL) isEqual: (id) object
{
	return [object isKindOfClass: [MSymbolExpr class]]
		&& [_symbol isEqual: ((MSymbolExpr*) object)->_symbol];
}

- (unsigned) hash
{
	return ~[_symbol hash] + [_symbol length];
}

- (NSString*) description
{
	return _symbol;
}

@end


@interface MDataExpr : MExpr
{
	NSData* _data;
}
@end

@implementation MDataExpr

- (id) initWithData: (NSData*) data
{
	NSParameterAssert(data);
	
	self = [super init];
	_data = [data copy];
	return self;
}

- (void) dealloc
{
	[_data release];
	[super dealloc];
}

- (NSData*) data
{
	return _data;
}

- (BOOL) put: (MMathLink*) link
{
	return [link putData: _data];
}

- (MExpr*) functionPart: (int) part
{
	return (part == 0 ? MSymbol(@"String") : nil);
}

- (BOOL) isEqual: (id) object
{
	return [object isKindOfClass: [MDataExpr class]]
		&& [_data isEqual: ((MDataExpr*) object)->_data];
}

- (unsigned) hash
{
	return ~[_data hash];
}

- (NSString*) description
{
	return [NSString stringWithFormat: @"\"%@\"",
		[[[NSString alloc] initWithData: _data encoding: NSASCIIStringEncoding] autorelease]];
}

@end


@interface MNumberExpr : MExpr
{
	NSNumber* _number;
}
@end

@implementation MNumberExpr

- (id) initWithNumber: (NSNumber*) number
{
	NSParameterAssert(number);
	
	self = [super init];
	_number = [number retain];
	return self;
}

- (void) dealloc
{
	[_number release];
	[super dealloc];
}

- (NSNumber*) number
{
	return _number;
}

- (NSNumber*) integer
{
	return ([_number isRealNumber] ? nil : _number);
}

- (NSNumber*) real
{
	return ([_number isRealNumber] ? _number : nil);
}

- (MExpr*) functionPart: (int) part
{
	return (part == 0 ? 
		([_number isRealNumber] ? MSymbol(@"Real") : MSymbol(@"Integer")) : nil);
}

- (BOOL) put: (MMathLink*) link
{
	return [link putNumber: _number];
}

- (BOOL) isEqual: (id) object
{
	return [object isKindOfClass: [MNumberExpr class]]
		&& [_number isEqual: ((MNumberExpr*) object)->_number];
}

- (unsigned) hash
{
	return ~[_number hash];
}

- (NSString*) description
{
	return [_number description];
}

@end


@interface MFunctionExpr : MExpr
{
	NSArray* _parts;
}
@end

@implementation MFunctionExpr

- (id) initWithParts: (NSArray*) parts
{
	NSParameterAssert(parts);
	
	self = [super init];
	_parts = [parts copy];
	return self;
}

- (void) dealloc
{
	[_parts release];
	[super dealloc];
}

- (int) functionLength
{
	return [_parts count] - 1;
}

- (MExpr*) functionPart: (int) part
{
	return [_parts objectAtIndex: part];
}

 - (BOOL) put: (MMathLink*) link
{
	int theLength = [self functionLength], i;
	
	if(![link putFunctionWithLength: theLength])
		return NO;
	
	for(i = 0; i <= theLength; i++)
		if(![[self functionPart: i] put: link])
			return NO;
	
	return YES;
}

- (BOOL) isEqual: (id) object
{
	return [object isKindOfClass: [MFunctionExpr class]]
		&& [_parts isEqual: ((MFunctionExpr*) object)->_parts];
}

- (unsigned) hash
{
	unsigned hash = 0;
	int i;
	for(i = [self functionLength]; i >= 0; i--)
		hash += [[self functionPart: i] hash];
	return hash;
}

- (NSString*) description
{
	NSMutableString* str = [NSMutableString string];
	[str appendString: [[self functionPart: 0] description]];
	[str appendString: @"["];
	
	int length = [self functionLength], i;
	for(i = 1; i <= length; i++)
	{
		[str appendString: [[self functionPart: i] description]];
		if(i < length)
			[str appendString: @", "];
	}
	
	[str appendString: @"]"];
	return str;
}

@end


@implementation MExpr

+ (MExpr*) exprWithString: (NSString*) string
{
	return [[[MStringExpr alloc] initWithString: string] autorelease];
}

+ (MExpr*) exprWithSymbol: (NSString*) symbol
{
	return [[[MSymbolExpr alloc] initWithSymbol: symbol] autorelease];
}

+ (MExpr*) exprWithData: (NSData*) data
{
	return [[[MDataExpr alloc] initWithData: data] autorelease];
}

+ (MExpr*) exprWithNumber: (NSNumber*) number
{
	return [[[MNumberExpr alloc] initWithNumber: number] autorelease];
}

+ (MExpr*) exprWithInteger: (int) integer
{
	return [MExpr exprWithNumber: [NSNumber numberWithInt: integer]];
}

+ (MExpr*) exprWithReal: (double) real
{
	return [MExpr exprWithNumber: [NSNumber numberWithDouble: real]];
}

+ (MExpr*) exprWithFunctionParts: (NSArray*) parts
{
	return [[[MFunctionExpr alloc] initWithParts: parts] autorelease];
}

MExpr* functionExpr(MExpr* head, va_list rest)
{
	NSMutableArray* parts = [NSMutableArray array];
	[parts addObject: head];
	
	MExpr* part;
	while((part = va_arg(rest, MExpr*)))
		[parts addObject: part];
	
	return [MExpr exprWithFunctionParts: parts];
}

+ (MExpr*) exprWithFunctionHead: (MExpr*) head, ...
{
	va_list rest;
	va_start(rest, head);
	MExpr* expr = functionExpr(head, rest);
	va_end(rest);
	return expr;
}

+ (MExpr*) exprWithFunctionName: (NSString*) name, ...
{
	va_list rest;
	va_start(rest, name);
	MExpr* expr = functionExpr([MExpr exprWithSymbol: name], rest);
	va_end(rest);
	return expr;
}

+ (MExpr*) exprWithFunctionName: (NSString*) name link: (MMathLink*) link
{
	NSMutableArray* parts = [NSMutableArray array];
	[parts addObject: [MExpr exprWithSymbol: name]];
	
	int count = [link expressionsToGet], i;
	for(i = 0; i < count; i++)
		[parts addObject: [MExpr exprWithLink: link]];
	
	return [MExpr exprWithFunctionParts: parts];
}

+ (MExpr*) exprWithLink: (MMathLink*) link
{
	switch([link type])
	{
		case eExprTypeString:
			return [MExpr exprWithString: [link string]];
		
		case eExprTypeSymbol:
			return [MExpr exprWithSymbol: [link symbol]];
		
		case eExprTypeInteger:
		case eExprTypeReal:
			return [MExpr exprWithNumber: [link number]];
		
		case eExprTypeFunction:
		{
			NSMutableArray* parts = [NSMutableArray array];
			int length = [link functionLength], i;
			for(i = 0; i <= length; i++)
			{
				id expr = [MExpr exprWithLink: link];
				if(expr == nil)
					return nil;
				[parts addObject: expr];
			}
			return [MExpr exprWithFunctionParts: parts];
		}
	}
	return nil;
}

- (id) copyWithZone: (NSZone*) zone
{
	return [self retain];
}

- (NSString*) string
{
	return nil;
}

- (NSString*) symbol
{
	return nil;
}

- (NSNumber*) number
{
	return nil;
}

- (NSNumber*) integer
{
	return nil;
}

- (NSNumber*) real
{
	return nil;
}

- (int) functionLength
{
	return -1;
}

- (MExpr*) functionPart: (int) part
{
	return nil;
}

- (BOOL) checkHead: (MExpr*) head
{
	return [head isEqual: [self functionPart: 0]];
}

- (BOOL) checkFunction: (MExpr*) head length: (int*) length
{
	if([self checkHead: head])
	{
		if(length)
			*length = [self functionLength];
		return YES;
	}
	return NO;
}

- (BOOL) put: (MMathLink*) link
{
	NSAssert(NO, @"base implementation should never be called");
	return NO;
}

@end


MExpr* MString(NSString* string)
{
	return [MExpr exprWithString: string];
}

MExpr* MSymbol(NSString* symbol)
{
	return [MExpr exprWithSymbol: symbol];
}

MExpr* MInteger(long integer)
{
	return [MExpr exprWithInteger: integer];
}

MExpr* MReal(double real)
{
	return [MExpr exprWithReal: real];
}

MExpr* MFunction(MExpr* head, ...)
{
	NSMutableArray*	parts = [NSMutableArray arrayWithCapacity: 0];
	va_list args;
	id arg;
	
	[parts addObject: head];
	
    va_start(args, head);
	while((arg = va_arg(args, id)) != nil)
		[parts addObject: arg];
    va_end(args);

	return [MExpr exprWithFunctionParts: parts];
}
